package com.chlitina.store.modules.inventory.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.chlitina.store.common.persistence.Page;
import com.chlitina.store.common.service.BaseService;
import com.chlitina.store.common.utils.DateUtils;
import com.chlitina.store.modules.base.dao.StoreProductDao;
import com.chlitina.store.modules.base.dao.StoreSelfProductDao;
import com.chlitina.store.modules.base.entity.StoreProduct;
import com.chlitina.store.modules.employee.dao.EmployeeDao;
import com.chlitina.store.modules.expert.dao.ProductDao;
import com.chlitina.store.modules.inventory.dao.InventoryInDao;
import com.chlitina.store.modules.inventory.dao.InventoryInDetailDao;
import com.chlitina.store.modules.inventory.dao.InventoryOutDao;
import com.chlitina.store.modules.inventory.dao.InventoryOutDetailDao;
import com.chlitina.store.modules.inventory.entity.StoreInventoryIn;
import com.chlitina.store.modules.inventory.entity.StoreInventoryInDetail;
import com.chlitina.store.modules.inventory.entity.StoreInventoryOut;
import com.chlitina.store.modules.inventory.entity.StoreInventoryOutDetail;
import com.chlitina.store.modules.inventory.model.InProduct;
import com.chlitina.store.modules.inventory.model.InQuery;
import com.chlitina.store.modules.inventory.model.InventoryIn;
import com.chlitina.store.modules.inventory.model.InventoryOut;
import com.chlitina.store.modules.inventory.model.OutProduct;
import com.chlitina.store.modules.inventory.model.OutQuery;
import com.chlitina.store.modules.sys.entity.Store;
import com.chlitina.store.modules.sys.utils.StoreUtils;

@Component
@Transactional(readOnly = true)
public class InventoryService extends BaseService {
	@Autowired
	private InventoryInDao inventoryInDao;
	@Autowired
	private InventoryInDetailDao inventoryInDetailDao;
	@Autowired
	private StoreProductDao storeProductDao;
	@Autowired
	private ProductDao productDao;
	@Autowired
	private StoreSelfProductDao selfProductDao;
	@Autowired
	private InventoryOutDao inventoryOutDao;
	@Autowired
	private InventoryOutDetailDao inventoryOutDetailDao;
	@Autowired 
	private EmployeeDao employeeDao;

	public StoreProduct findStoreProduct(List<StoreProduct> storeSysProducts,
			InProduct inProduct) {
		for (StoreProduct storeProduct : storeSysProducts) {
			if (storeProduct.getId().longValue() == inProduct.getProduct()
					.getId().longValue()) {
				return storeProduct;
			}
		}
		return null;
	}

	public StoreProduct createStoreProduct(InProduct inProduct, Store store) {
		storeProductDao.refresh(inProduct.getProduct());
		return inProduct.getProduct();
	}

	private List<StoreInventoryInDetail> createDetails(
			StoreInventoryIn inventoryIn, InventoryIn in) {
		List<StoreInventoryInDetail> list = new ArrayList<StoreInventoryInDetail>();
		for (InProduct inProduct : in.getProductList()) {
			StoreProduct storeProduct = inProduct.getProduct();
			if (storeProduct != null) {// update
				inventoryIn.setAmount(inventoryIn.getAmount()
						+ inProduct.getAmount());
					storeProduct.setAmount(storeProduct.getAmount()
							+ inProduct.getAmount());
				storeProductDao.save(storeProduct);
				storeProductDao.flush();
			} else {
				System.err.println("CAN NOT FIND PRODUCT ID "
						+ inProduct.getProduct().getId());
				continue;
			}
			StoreInventoryInDetail storeInventoryInDetail = new StoreInventoryInDetail();
			storeInventoryInDetail.setAmount(inProduct.getAmount());
			storeInventoryInDetail.setStoreProduct(storeProduct);
			storeInventoryInDetail.setStoreInventoryIn(inventoryIn);
			list.add(storeInventoryInDetail);
		}
		return list;
	}

	@Transactional(readOnly = false)
	public StoreInventoryOut out(InventoryOut out) {
		StoreInventoryOut inventoryOut = new StoreInventoryOut();
		inventoryOut.setEmployee(employeeDao.findOne(out.getEmployee().getId()));
		inventoryOut.setOutDate(new Date());
		inventoryOut.setRemark(out.getRemark());
		inventoryOut.setStore(StoreUtils.getStore());
		List<OutProduct> failed = new ArrayList<OutProduct>();
		List<OutProduct> success = new ArrayList<OutProduct>();
		List<StoreInventoryOutDetail> details = createDetails(inventoryOut,
				out, failed, success);

		inventoryOutDao.save(inventoryOut);
		inventoryOutDetailDao.save(details);

		if (!success.isEmpty()) {
			for (OutProduct op : success) {
				StoreProduct product = op.getProduct();
				storeProductDao.save(product);
			}
			storeProductDao.flush();
		}

		// debug
		System.err.println("DEBUG-----Success-" + success.size());
		for (OutProduct op : success) {
			System.out.println("StoreProduct-" + op.getProduct().getId() + "--"
					+ op.getAmount());
		}
		System.err.println("DEBUG--------Failed-" + failed.size());
		for (OutProduct op : failed) {
			System.out.println("StoreProduct-" + op.getProduct().getId() + "--"
					+ op.getAmount());
		}
		return inventoryOut;
	}

	private List<StoreInventoryOutDetail> createDetails(
			StoreInventoryOut inventoryOut, InventoryOut out,
			List<OutProduct> failed, List<OutProduct> success) {
		List<StoreInventoryOutDetail> details = new ArrayList<StoreInventoryOutDetail>();
		List<OutProduct> all = out.getAll();
		for (OutProduct outProduct : all) {
			if (!outProduct.out()) {
				failed.add(outProduct);
				continue;
			}
			inventoryOut.setAmount(inventoryOut.getAmount()
					+ outProduct.getAmount());
			success.add(outProduct);
			StoreInventoryOutDetail detail = new StoreInventoryOutDetail();
			detail.setAmount(outProduct.getAmount());
			detail.setStoreInventoryOut(inventoryOut);
			detail.setStoreProduct(outProduct.getProduct());
			int product_type = outProduct.getProduct().getProduct_type();
			if (product_type == StoreProduct.TYPE_SYS) {
				detail.setRetailPrice(outProduct.getProduct().getProduct()
						.getRetail_price());
			} else {
				detail.setRetailPrice(outProduct.getProduct()
						.getStoreSelfProduct().getRetail_price());
			}
			details.add(detail);
		}
		return details;
	}

	@Transactional(readOnly = false)
	public StoreInventoryIn in(InventoryIn in) {
		StoreInventoryIn inventoryIn = new StoreInventoryIn();
		inventoryIn.setEmployee(employeeDao.findOne(in.getEmployee().getId()));
		inventoryIn
				.setInProductChannel(in.isSystemProduct() ? StoreInventoryIn.IN_SYSTEM
						: StoreInventoryIn.IN_SELF);
		inventoryIn.setRemark(in.getRemark());
		inventoryIn.setStore(StoreUtils.getStore());
		inventoryIn.setInDate(new Date());
		// date
		List<StoreInventoryInDetail> details = createDetails(inventoryIn, in);
		inventoryInDao.save(inventoryIn);
		inventoryInDetailDao.save(details);
		return inventoryIn;

	}

	public void inQuery(Page<StoreInventoryIn> page, InQuery inQuery) {
		DetachedCriteria dc = inventoryInDao.createDetachedCriteria();
		List<StoreInventoryIn> storeInventoryIns = inQuery
				.getStoreInventoryIns();
		storeInventoryIns.clear();
		String startDate = inQuery.getStartDate();
		String endDate = inQuery.getEndDate();
		if (startDate != null && !startDate.isEmpty() || endDate != null
				&& !endDate.isEmpty()) {
			Date start = null;
			Date end = null;
			try {
				start = DateUtils.getBeginOfDay(DateUtils.parseDate(startDate));
			} catch (Exception e) {
			}
			try {
				end = DateUtils.getEndOfDay(DateUtils.parseDate(endDate));
			} catch (Exception e) {
			}
			if (start != null && end == null) {
				dc.add(Restrictions.ge("inDate", start));
			} else if (start == null && end != null) {
				dc.add(Restrictions.le("inDate", end));
			} else if (start != null && end != null) {
				dc.add(Restrictions.between("inDate", start, end));
			}
		}

		Store store = StoreUtils.getStore();
		dc.add(Restrictions.eq("store", store));
		dc.addOrder(Order.desc("id"));
		
		page.setOrderBy("inDate DESC");
		Page<StoreInventoryIn> find = inventoryInDao.find(page, dc);
		storeInventoryIns.addAll(find.getList());
	}

	public List<StoreInventoryInDetail> findDetails(int storeInventoryInId) {
		return inventoryInDetailDao.findListBySII(storeInventoryInId);
	}

	public void outQuery(Page<StoreInventoryOut> page, OutQuery outQuery) {
		DetachedCriteria dc = inventoryOutDao.createDetachedCriteria();
		List<StoreInventoryOut> storeInventoryOuts = outQuery
				.getStoreInventoryOuts();
		storeInventoryOuts.clear();
		String startDate = outQuery.getStartDate();
		String endDate = outQuery.getEndDate();
		if (startDate != null && !startDate.isEmpty() || endDate != null
				&& !endDate.isEmpty()) {
			Date start = null;
			Date end = null;
			try {
				start = DateUtils.getBeginOfDay(DateUtils.parseDate(startDate));
			} catch (Exception e) {
			}
			try {
				end = DateUtils.getEndOfDay(DateUtils.parseDate(endDate));
			} catch (Exception e) {
			}
			if (start != null && end == null) {
				dc.add(Restrictions.ge("outDate", start));
			} else if (start == null && end != null) {
				dc.add(Restrictions.le("outDate", end));
			} else if (start != null && end != null) {
				dc.add(Restrictions.between("outDate", start, end));
			}
		}

		Store store = StoreUtils.getStore();
		dc.add(Restrictions.eq("store", store));
		dc.addOrder(Order.desc("id"));
		
		page.setOrderBy("outDate DESC");
		Page<StoreInventoryOut> find = inventoryOutDao.find(page, dc);
		storeInventoryOuts.addAll(find.getList());
	}

	public List<StoreInventoryOutDetail> findOutDetails(int parseInt) {
		return inventoryOutDetailDao.findListBySIO(parseInt);
	}

	public void deleteInventoryOut(long outID) {
		// System.err.println(outID);
		inventoryOutDetailDao
				.updateBySql("DELETE FROM store_inventory_out_detail where SIO_ID="
						+ outID);
		inventoryOutDetailDao.flush();
		inventoryOutDetailDao
				.updateBySql("DELETE FROM store_inventory_out where ID="
						+ outID);
		inventoryOutDao.flush();

	}

	public String inventoryInRed(long id) {
		StoreInventoryIn target = inventoryInDao.findOne(id);
		if (target == null || target.getStatus() == StoreInventoryIn.RED_STATUS) {
			return "";
		}
		DetachedCriteria criteria = inventoryInDetailDao
				.createDetachedCriteria();
		criteria.add(Restrictions.eq("storeInventoryIn", target));
		List<StoreInventoryInDetail> details = inventoryInDetailDao
				.find(criteria);
		StringBuilder sb = new StringBuilder();

		for (StoreInventoryInDetail detail : details) {
			int amount = detail.getAmount();
			StoreProduct storeProduct = detail.getStoreProduct();
			if (storeProduct.getAmount() < amount) {
				if (storeProduct.getProduct_type() == StoreProduct.TYPE_SYS) {
					sb.append(storeProduct.getProduct().getName() + ";");
				}
			}
		}
		if (!"".equals(sb.toString())) {
			return sb.toString();
		}
		target.setStatus(StoreInventoryIn.RED_STATUS);
		inventoryInDao.save(target);
		inventoryInDao.flush();
		for (StoreInventoryInDetail detail : details) {
			int amount = detail.getAmount();
			StoreProduct storeProduct = detail.getStoreProduct();
			storeProduct.setAmount(storeProduct.getAmount() - amount);
			storeProductDao.save(storeProduct);
			storeProductDao.flush();
		}
		return "";
	}

}
